#include "ssd1322_io_hz.h"

ssd1322_io_hz::ssd1322_io_hz(uint8_t scl, uint8_t sda, uint8_t cs, uint8_t dc, uint8_t zk_cs, uint8_t zk_sda)
        : ESP8266_SSD1322(0, sda, scl, dc, 0, cs), zk_cs(zk_cs), zk_sda(zk_sda) {
    hwSPI = false;
}

void ssd1322_io_hz::ssd1322_command(uint8_t dat) {
    digitalWrite(dc, LOW);
    digitalWrite(cs, LOW);
    for (volatile int d = 0; d < 0x10; d++);
    for (int i = 0; i < 8; i++) {
        digitalWrite(sclk, LOW);
        for (volatile int d = 0; d < 0x10; d++);
        if (dat & 0x80)
            digitalWrite(sda, HIGH);
        else
            digitalWrite(sda, LOW);
        digitalWrite(sclk, HIGH);
        for (volatile int d = 0; d < 0x10; d++);
        dat <<= 1;
    }
    digitalWrite(cs, HIGH);
    digitalWrite(dc, HIGH);
    for (volatile int d = 0; d < 0x10; d++);
}

void ssd1322_io_hz::ssd1322_data(uint8_t dat) {
    digitalWrite(dc, HIGH);
    digitalWrite(cs, LOW);
    for (volatile int d = 0; d < 0x10; d++);
    for (int i = 0; i < 8; i++) {
        digitalWrite(sclk, LOW);
        for (volatile int d = 0; d < 0x10; d++);
        if (dat & 0x80)
            digitalWrite(sda, HIGH);
        else
            digitalWrite(sda, LOW);
        digitalWrite(sclk, HIGH);
        for (volatile int d = 0; d < 0x10; d++);
        dat <<= 1;
    }
    digitalWrite(cs, HIGH);
    digitalWrite(dc, HIGH);
    for (volatile int d = 0; d < 0x10; d++);
}

void ssd1322_io_hz::Display_Asc(unsigned char zk_num, unsigned int x, u8 y) {
    //extern unsigned char  FontBuf[128];//字库缓存
    unsigned char j;
    unsigned char x0;
    unsigned char y0;
    unsigned char n;
    unsigned char k;
    unsigned int x1 = x / 4;

    switch (zk_num) {

        case 2:
        case '2':
            x0 = 1;
            y0 = 7;
            n = 8;
            k = 1;
            break; //	  7x8 ASCII
        case 3:
        case '3':
            x0 = 1;
            y0 = 11;
            n = 12;
            k = 1;
            break; //  6x12 ASCII
        case 4:
        case '4':
            x0 = 1;
            y0 = 15;
            n = 16;
            k = 1;
            break; //  8x16 ASCII
        case 5:
        case '5':
            x0 = 3;
            y0 = 23;
            n = 48;
            k = 1;
            break; //  12x24 ASCII
        case 6:
        case '6':
            x0 = 3;
            y0 = 31;
            n = 64;
            k = 1;
            break; //  16x32 ASCII
        default:
            //                case 1:
            //                    case '1':
            x0 = 1;
            y0 = 7;
            n = 8;
            k = 0;
            break; //	  5x7 ASCII
    }

    Set_Column_Address((u8) (Shift + x1), (u8) (Shift + x1 + x0)); // 设置列坐标，shift为列偏移量由1322决定。
    Set_Row_Address(y, y + y0);                                  //设置行坐标
    Set_Write_RAM();                                             //	写显存

    if (k == 0) {
        for (j = 0; j < n - 1; j++) {
            Con_4_byte(FontBuf[j]); //数据转换，把数组数据送入SSD1322
        }
        ssd1322_data(0x00); //写前2列
        ssd1322_data(0x00); //写后2列	  共计4列
    } else {
        for (j = 0; j < n; j++) {
            Con_4_byte(FontBuf[j]); //数据转换，把数组数据送入SSD1322
        }
    }
}

void ssd1322_io_hz::Display_GBasc(unsigned char zk_num, unsigned int x, u8 y) {
    unsigned char x0;
    unsigned char y0;
    unsigned char n;
    unsigned int x1 = x / 4;

    switch (zk_num) {

        case 2:
        case '2':
        case '3':
        case 3:
            x0 = 1;
            y0 = 15;
            n = 16;
            break;
        case '4':
        case 4:
            x0 = 3;
            y0 = 23;
            n = 48;
            break;
        case '5':
        case 5:
            x0 = 3;
            y0 = 31;
            n = 64;
            break;
        default:
            //                case 1:
            //                    case '1':
            x0 = 1;
            y0 = 11;
            n = 12;
            break;
    }

    Set_Column_Address((u8) (Shift + x1), (u8) (Shift + x1 + x0)); // 设置列坐标，shift为列偏移量由1322决定。
    Set_Row_Address(y, y + y0);                                  //设置行坐标
    Set_Write_RAM();                                             //	写显存

    for (int j = 0; j < n; j++) {
        Con_4_byte(FontBuf[j]); //数据转换，把数组数据送入SSD1322
    }
}

void ssd1322_io_hz::Display_GB2312(unsigned char zk_num, unsigned int x, u8 y) {
    unsigned int n;
    unsigned int x0;
    u8 y0;
    unsigned int x1 = x / 4;

    switch (zk_num) {

        case '2':
        case 2:
            x0 = 3;
            y0 = 15;
            n = 32;
            break;
        case '3':
        case 3:
            x0 = 5;
            y0 = 23;
            n = 72;
            break;
        case '4':
        case 4:
            x0 = 7;
            y0 = 31;
            n = 128;
            break;
        default:
            //        case 1:
            //        case '1':
            x0 = 3;
            y0 = 11;
            n = 24;
            break;
    }

    Set_Column_Address((u8) (Shift + x1), (u8) (Shift + x1 + x0)); // 设置列坐标，shift为列偏移量由1322决定。
    Set_Row_Address(y, (u8) (y + y0));                            //设置行坐标
    Set_Write_RAM();                                             //	写显存

    for (int j = 0; j < n; j++) {
        Con_4_byte(FontBuf[j]); //数据转换，把数组数据送入SSD1322
    }
}

void ssd1322_io_hz::get_n_bytes_data_from_ROM(unsigned char AddrHigh, unsigned char AddrMid, unsigned char AddrLow,
                                              unsigned char *pBuff, unsigned char DataLen) const {
    digitalWrite(sclk, HIGH); //字库时钟拉低
    digitalWrite(zk_cs, LOW); //字库片选
    for (volatile int d = 0; d < 0x10; d++);
    ZK_command(0x03);     //写指令
    ZK_command(AddrHigh); //写地址高字节
    ZK_command(AddrMid);  //写地址中字节
    ZK_command(AddrLow);  //写地址低字节
    // ZK_command(0xff);      //写地址低字节
    for (int i = 0; i < DataLen; i++)
        *(pBuff + i) = get_data_from_ROM(); //读一个字节数据
    digitalWrite(zk_cs, HIGH);              //取消字库片选
}

unsigned char ssd1322_io_hz::get_data_from_ROM() const {
    u32 ret_data = 0;         //返回数据初始化
    digitalWrite(sclk, HIGH); //字库时钟拉高
    pinMode(zk_sda, INPUT_PULLDOWN);
    for (int i = 0; i < 8; i++) {
        digitalWrite(sclk, LOW); //字库时钟拉低
        for (volatile int d = 0; d < 0x10; d++);
        ret_data = ret_data << 1;
        if (digitalRead(zk_sda)) {
            ret_data = ret_data + 1;
        } else {
            ret_data = ret_data + 0;
        }
        digitalWrite(sclk, HIGH); //字库时钟拉高
        for (volatile int d = 0; d < 0x10; d++);
    }
    return (unsigned char) ret_data; //返回读出的一个字节
}

void ssd1322_io_hz::ZK_command(unsigned char dat) const {
    u32 data = dat;
    for (int i = 0; i < 8; i++) {
        digitalWrite(sclk, LOW); //字库时钟拉低
        if (data & 0x80) {
            digitalWrite(sda, HIGH);
        } else {
            digitalWrite(sda, LOW);
        }
        for (volatile int d = 0; d < 0x10; d++);
        data = data << 1;

        for (volatile int d = 0; d < 0x10; d++);
        digitalWrite(sclk, HIGH); //字库时钟拉高
        for (volatile int d = 0; d < 0x10; d++);
    }
}

void ssd1322_io_hz::Con_4_byte(unsigned char DATA) {
    std::array<unsigned char, 4> d1_4byte{};
    std::array<unsigned char, 4> d2_4byte{};
    u32 d;
    unsigned char k1;
    unsigned char k2;
    d = DATA;

    for (int i = 0; i < 2; i++) // 一两位的方式写入  2*4=8位
    {
        k1 = d & 0xc0; //当i=0时 为D7,D6位 当i=1时 为D5,D4位

        /****有4种可能，16级灰度,一个字节数据表示两个像素，一个像素对应一个字节的4位***/

        switch (k1) {
            case 0x00:
                d1_4byte[i] = 0x00;

                break;
            case 0x40: // 0100,0000
                d1_4byte[i] = 0x0f;

                break;
            case 0x80: //0x01,0000
                d1_4byte[i] = 0xf0;

                break;
            default:
                //            case 0xc0: //1100,0000
                d1_4byte[i] = 0xff;

                break;
        }

        d = d << 2;
        k2 = d & 0xc0; //当i=0时 为D7,D6位 当i=1时 为D5,D4位

        /****有4种可能，16级灰度,一个字节数据表示两个像素，一个像素对应一个字节的4位***/

        switch (k2) {
            case 0x00:
                d2_4byte[i] = 0x00;

                break;
            case 0x40: // 0100,0000
                d2_4byte[i] = 0x0f;

                break;
            case 0x80: //0x01,0000
                d2_4byte[i] = 0xf0;

                break;
                //            case 0xc0: //1100,0000
            default:
                d2_4byte[i] = 0xff;

                break;
        }

        d = d << 2; //左移两位

        ssd1322_data(d1_4byte[i]); //写前2列
        ssd1322_data(d2_4byte[i]); //写后2列	  共计4列
    }
}

void ssd1322_io_hz::Set_Read_RAM() {
    ssd1322_command(0x5D); // Enable MCU to Read from RAM
}

void ssd1322_io_hz::Set_Write_RAM() {

    ssd1322_command(0x5C); // Enable MCU to Write into RAM
}

void ssd1322_io_hz::Set_Row_Address(unsigned char a, unsigned char b) {
    ssd1322_command(0x75); // Set Row Address
    ssd1322_data(a);       //   Default => 0x00
    ssd1322_data(b);       //   Default => 0x7F
}

void ssd1322_io_hz::Set_Column_Address(unsigned char a, unsigned char b) {
    ssd1322_command(0x15); // Set Column Address
    ssd1322_data(a);       //   Default => 0x00
    ssd1322_data(b);       //   Default => 0x77
}

void ssd1322_io_hz::begin(uint8_t i2caddr, bool reset) {
    Shift = 0x1c;
    pinMode(zk_cs, OUTPUT);
    pinMode(zk_sda, INPUT_PULLDOWN);
    pinMode(sclk, OUTPUT);
    pinMode(sda, OUTPUT);
    digitalWrite(zk_cs, HIGH);
    digitalWrite(sclk, HIGH);
    digitalWrite(sda, HIGH);
    ESP8266_SSD1322::begin(i2caddr, reset);
}

void ssd1322_io_hz::ssd1322_dataBytes(uint8_t *data, const uint32_t size) {
    digitalWrite(dc, HIGH);
    digitalWrite(cs, LOW);
    for (uint32_t ii = 0; ii < size; ii++) {
        uint8_t dat = data[ii];
        for (int i = 0; i < 8; i++) {
            digitalWrite(sclk, LOW);
            if (dat & 0x80)
                digitalWrite(sda, HIGH);
            else
                digitalWrite(sda, LOW);
            digitalWrite(sclk, HIGH);
            dat <<= 1;
        }
    }
    digitalWrite(cs, HIGH);
    digitalWrite(dc, HIGH);
}

int ssd1322_io_hz::drawString(char *string, int16_t poX, int16_t poY, int size) {
    unsigned char i = 0;
    int pos = 0;
    while (string[i]) {
        if (((string[i] >= 0xA1) && (string[i] <= 0xA9)) && (string[i + 1] >= 0xA1)) {
            pos += drawHz1(&string[i], poX + pos, (u8) poY, size);
            i += 2;
        } else if (((string[i] >= 0xB0) && (string[i] <= 0xF7)) && (string[i + 1] >= 0xA1)) {
            pos += drawHz2(&string[i], poX + pos, (u8) poY, size);
            i += 2;
        } else {
            pos += drawAsc(string[i], poX + pos, (u8) poY, size);
            i++;
        }
    }
    return pos;
}

int ssd1322_io_hz::drawHz1(const char *string, int poX, u8 poY, int size) {
    unsigned char AddrHigh;
    unsigned char AddrMid;
    unsigned char AddrLow; //字高、中、低地址

    unsigned long FontAddr = 0; //字地址
    unsigned long BaseAdd = 0;  //字库基地址
    unsigned char n = 0;
    unsigned char d = 0; // 不同点阵字库的计算变量
    switch (size) {
        case '2':
        case 2:
            BaseAdd = 0x2C9D0;
            n = 32;
            d = 16;
            break;
        case '3':
        case 3:
            BaseAdd = 0x68190;
            n = 72;
            d = 24;
            break;
        case '4':
        case 4:
            BaseAdd = 0xEDF00;
            n = 128;
            d = 32;
            break;
        default:
            //                case '1':
            BaseAdd = 0x0;
            n = 24;
            d = 12;
            break;
    }
    /*国标简体（GB2312）汉字在 字库IC中的地址由以下公式来计算：*/
    /*Address = ((MSB - 0xA1) * 94 + (LSB - 0xA1))*n+ BaseAdd; 分三部取地址*/
    FontAddr = (string[0] - 0xA1) * 94;
    FontAddr += (string[1] - 0xA1);
    FontAddr = ((FontAddr * n) + BaseAdd);

    AddrHigh = (FontAddr & 0xff0000) >> 16;                                   /*地址的高8位,共24位*/
    AddrMid = (FontAddr & 0xff00) >> 8;                                       /*地址的中8位,共24位*/
    AddrLow = FontAddr & 0xff;                                                /*地址的低8位,共24位*/
    get_n_bytes_data_from_ROM(AddrHigh, AddrMid, AddrLow, FontBuf.data(), n); /*取一个汉字的数据，存到"FontBuf[]"*/
    Display_GB2312((unsigned char) size, poX, poY);                            //显示一个汉字到OLED上/
    return d;
}

int ssd1322_io_hz::drawHz2(const char *string, int poX, u8 poY, int size) {
    unsigned char AddrHigh;
    unsigned char AddrMid;
    unsigned char AddrLow; //字高、中、低地址

    unsigned long FontAddr = 0; //字地址
    unsigned long BaseAdd = 0;  //字库基地址
    unsigned char n = 0;
    unsigned char d = 0; // 不同点阵字库的计算变量
    switch (size) {
        case 2:
        case '2':
            BaseAdd = 0x2C9D0;
            n = 32;
            d = 16;
            break;
        case '3':
        case 3:
            BaseAdd = 0x68190;
            n = 72;
            d = 24;
            break;
        case '4':
        case 4:
            BaseAdd = 0xEDF00;
            n = 128;
            d = 32;
            break;
        default:
            //            case '1':
            BaseAdd = 0x0;
            n = 24;
            d = 12;
            break;
    }
    /*国标简体（GB2312） 字库IC中的地址由以下公式来计算：*/
    /*Address = ((MSB - 0xB0) * 94 + (LSB - 0xA1)+846)*n+ BaseAdd; 分三部取地址*/

    FontAddr = (string[0] - 0xB0) * 94;
    FontAddr += (string[1] - 0xA1) + 846;
    FontAddr = ((FontAddr * n) + BaseAdd);

    AddrHigh = (FontAddr & 0xff0000) >> 16;                                   /*地址的高8位,共24位*/
    AddrMid = (FontAddr & 0xff00) >> 8;                                       /*地址的中8位,共24位*/
    AddrLow = FontAddr & 0xff;                                                /*地址的低8位,共24位*/
    get_n_bytes_data_from_ROM(AddrHigh, AddrMid, AddrLow, FontBuf.data(), n); /*取一个汉字的数据，存到"FontBuf[ ]"*/
    Display_GB2312((unsigned char) size, poX, poY);                            //显示一个汉字到OLED上/
    return d;
}

int ssd1322_io_hz::drawAsc(char str, int poX, u8 poY, int size) {
    unsigned short FontCode; // 字符内码
    unsigned char AddrHigh;
    unsigned char AddrMid;
    unsigned char AddrLow; //字高、中、低地址

    unsigned long FontAddr = 0; //字地址
    unsigned long BaseAdd = 0;  //字库基地址
    unsigned char n = 0;
    unsigned char d;

    switch (size) {

        case '2':
        case 2:
            BaseAdd = 0x1DD790;
            n = 16;
            d = 8;
            break; //8X16 点国标扩展字符
        case '3':
        case 3:
            BaseAdd = 0x1F2880;
            n = 16;
            d = 8;
            break; //8X16 点国标扩展特殊字符
        case '4':
        case 4:
            BaseAdd = 0x1DFF30;
            n = 48;
            d = 12;
            break; //12X24 点国标扩展字符
        case '5':
        case 5:
            BaseAdd = 0x1E5A90;
            n = 64;
            d = 16;
            break; // 16X32 点国标扩展字符
        default:
            //        case '1':
            //        case 1:
            BaseAdd = 0x1DBE0C;
            n = 12;
            d = 8;
            break; //	   6X12 点国标扩展字符
    }

    FontCode = (u16) (0xAA00 | str | 0x80); //ascii最高位为1
    if ((FontCode >= 0xAAA1) && (FontCode <= 0xAAFE)) {
        FontAddr = FontCode - 0xAAA1;
        FontAddr = ((FontAddr * n) + BaseAdd);
        AddrHigh = (FontAddr & 0xff0000) >> 16;                                   /*地址的高8位,共24位*/
        AddrMid = (FontAddr & 0xff00) >> 8;                                       /*地址的中8位,共24位*/
        AddrLow = FontAddr & 0xff;                                                /*地址的低8位,共24位*/
        get_n_bytes_data_from_ROM(AddrHigh, AddrMid, AddrLow, FontBuf.data(), n); /*取一个汉字的数据，存到"FontBuf[]"*/
        Display_GBasc((unsigned char) size, poX, poY);                             /*显示一个字符到OLED上 */
    } else if ((FontCode >= 0xABA1) && (FontCode <= 0xABC0)) {
        FontAddr = (FontCode - 0xABA1) + 95;
        FontAddr = ((FontAddr * n) + BaseAdd);
        AddrHigh = (FontAddr & 0xff0000) >> 16;                                   /*地址的高8位,共24位*/
        AddrMid = (FontAddr & 0xff00) >> 8;                                       /*地址的中8位,共24位*/
        AddrLow = FontAddr & 0xff;                                                /*地址的低8位,共24位*/
        get_n_bytes_data_from_ROM(AddrHigh, AddrMid, AddrLow, FontBuf.data(), n); /*取一个汉字的数据，存到"FontBuf[]"*/
        Display_GBasc((unsigned char) size, poX, poY);                             /*显示一个字符到OLED上 */
    } else if ((FontCode >= 0xACA1) && (FontCode <= 0xACDF)) // 8X16 点国标扩展特殊字符
    {
        FontAddr = FontCode - 0xACA1;
        FontAddr = ((FontAddr * n) + BaseAdd);
        AddrHigh = (FontAddr & 0xff0000) >> 16;                                   //地址的高8位,共24位/
        AddrMid = (FontAddr & 0xff00) >> 8;                                       //地址的中8位,共24位/
        AddrLow = FontAddr & 0xff;                                                //地址的低8位,共24位/
        get_n_bytes_data_from_ROM(AddrHigh, AddrMid, AddrLow, FontBuf.data(), n); //取一个汉字的数据，存到"FontBuf[]"/
        Display_GBasc((unsigned char) size, poX, poY);                             //显示一个字符到OLED上 /
    }
    return d;
}

int ssd1322_io_hz::drawCentreString(char *string, int16_t dX, int16_t poY, int size) {
    auto len = (int16_t) (strlen(string) * 8);
    int16_t poX = dX - len / 2;

    if (poX < 0)
        poX = 0;
    return drawString(string, poX, poY, size);
}

int ssd1322_io_hz::drawChar(char c, int16_t x, int16_t y, int size) {
    return drawAsc(c, x, (u8) y, size);
}

void ssd1322_io_hz::drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size_x,
                             uint8_t size_y) {
    drawAsc(c, x, (u8) y, size_x);
}
